package cn.hsw.component.core.util;

import cn.hutool.core.net.NetUtil;

/**
 * twitter的snowflake算法 -- java实现
 * @author 李子木
 * @date 2020-11-03
 */
public class SnowFlakeUtil {

    /**
     * 起始的时间戳
     */
    private final static long START_TIME_STAMP = 1604370284000L;

    /**
     * 每一部分占用的位数
     *      序列号占用的位数
     *      机器标识占用的位数
     *      数据中心占用的位数
     */
    private final static long SEQUENCE_BIT = 12;
    private final static long MACHINE_BIT = 5;
    private final static long DATA_CENTER_BIT = 5;

    /**
     * 每一部分的最大值
     */
    private final static long MAX_DATACENTER_NUM = ~(-1L << DATA_CENTER_BIT);
    private final static long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT);
    private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);

    /**
     * 每一部分向左的位移
     */
    private final static long MACHINE_LEFT = SEQUENCE_BIT;
    private final static long DATA_CENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
    private final static long TIME_STAMP_LEFT = DATA_CENTER_LEFT + DATA_CENTER_BIT;

    /**
     * 数据中心
     */
    private static long dataCenterId = 1L;
    /**
     * 机器标识
     */
    private static long machineId =0L;
    /**
     * 序列号
     */
    private long sequence = 0L;
    /**
     * 上一次时间戳
     */
    private long lastTimeStamp = -1L;

    static {
        machineId = NetUtil.ipv4ToLong(NetUtil.getLocalhostStr())%32;
    }


    private static final SnowFlakeUtil INSTANCE = new SnowFlakeUtil(dataCenterId,machineId);

    public static SnowFlakeUtil getInstance() {
        return INSTANCE;
    }

    /**
     * @param dataCenterId 数据中心ID
     * @param machineId 机器标识
     */
    public SnowFlakeUtil(long dataCenterId, long machineId) {
        if (dataCenterId > MAX_DATACENTER_NUM || dataCenterId < 0) {
            throw new IllegalArgumentException("数据中心ID 需要在0和"+MAX_DATACENTER_NUM+"之间");
        }
        if (machineId > MAX_MACHINE_NUM || machineId < 0) {
            throw new IllegalArgumentException("机器标识 需要在0和"+MAX_MACHINE_NUM+"之间");
        }
        SnowFlakeUtil.dataCenterId = dataCenterId;
        SnowFlakeUtil.machineId = machineId;
    }

    /**
     * 产生下一个ID
     *
     * @return id
     */
    public synchronized long nextId() {
        long currTimeStamp = getTime();
        if (currTimeStamp < lastTimeStamp) {
            throw new RuntimeException("系统时钟改变.  无法生成ID");
        }

        if (currTimeStamp == lastTimeStamp) {
            //相同毫秒内，序列号自增
            sequence = (sequence + 1) & MAX_SEQUENCE;
            //同一毫秒的序列数已经达到最大
            if (sequence == 0L) {
                currTimeStamp = getNextMill();
            }
        } else {
            //不同毫秒内，序列号置为0
            sequence = 0L;
        }

        lastTimeStamp = currTimeStamp;

        /*时间戳部分**数据中心部分**机器标识部分**序列号部分*/
        return (currTimeStamp - START_TIME_STAMP) << TIME_STAMP_LEFT
                | dataCenterId << DATA_CENTER_LEFT
                | machineId << MACHINE_LEFT
                | sequence;
    }

    /**
     * 获取下一个时间
     * @return 时间戳
     */
    private long getNextMill() {
        long mill = getTime();
        while (mill <= lastTimeStamp) {
            mill = getTime();
        }
        return mill;
    }

    /**
     * 获取系统当前时间
     * @return 时间戳
     */
    private long getTime() {
        return System.currentTimeMillis();
    }

    public static void main(String[] args) {
        for (int i=0;i<1000000000;i++){
            long id = SnowFlakeUtil.getInstance().nextId();
            System.out.println(id);
        }
    }
}
